#pragma once
#include "level.hpp"
#include "out.hpp"
#include "format.hpp"
#include "worker.hpp"
#include <unordered_map>
#include <atomic>
#include <mutex>
#include <cstdarg>
#include <cassert>

namespace lcllog
{
    class Logger
    {
    public:
        using ptr = std::shared_ptr<Logger>;
        Logger(const std::string &logger_name, 
            Formatter::ptr formatter,
            std::vector<LogOut::ptr> &outs, 
            LogLevel::value limit_level = LogLevel::value::DEBUG)
            : _logger_name(logger_name)
            ,  _limit_level(limit_level)
            , _formatter(formatter)
            ,_outs(outs.begin(), outs.end())
        {}
        const std::string loggerName() { return _logger_name; }
        //完成构造日志消息对象过程并进行格式化，得到格式化后的日志消息字符串--然后进行落地输出
        void debug(const std::string& file, size_t line, const std::string& fmt, ...)
        {
            //通过传入的参数构造出一个日志消息对象，进行日志的格式化，最终落地
            //1.判断当前的日志是否达到了输出等级
            if(LogLevel::value::DEBUG < _limit_level) return;
            //2.对fmt格式化字符串和不定参进行字符串组织，得到的日志消息的字符串
            va_list vl; 
            va_start(vl, fmt); 
            char* tmp; 
            int ret = vasprintf(&tmp, fmt.c_str(), vl); 
            if(ret == -1)
            {
                std::cout << "vasprintf file!" << std::endl;
                return;
            }
            va_end(vl);
            /*
            //3.构造LogMsg对象
            LogMsg msg(_logger_name, file, line, LogLevel::value::DEBUG , tmp);
            //4.通过格式化工具对LogMsg进行格式化，得到格式化后的日志字符串
            std::stringstream ss;
            _formatter->format(ss, msg);
            //5.进行日志落地
            log(ss.str().c_str(), ss.str().size());
            */
           serialize(LogLevel::value::DEBUG, file, line, tmp);
           free(tmp);
        }
        void info(const std::string& file, size_t line, const std::string& fmt, ...)
        {
            if(LogLevel::value::INFO < _limit_level) return;
            va_list vl;
            va_start(vl, fmt);
            char* tmp; 
            int ret = vasprintf(&tmp, fmt.c_str(), vl); 
            if(ret == -1)
            {
                std::cout << "vasprintf file!" << std::endl;
                return;
            }
            va_end(vl);
           serialize(LogLevel::value::INFO, file, line, tmp);
           free(tmp);
        }
        void warn(const std::string& file, size_t line, const std::string& fmt, ...)
        {
            if(LogLevel::value::WARN < _limit_level) return;
            va_list vl;
            va_start(vl, fmt);
            char* tmp; 
            int ret = vasprintf(&tmp, fmt.c_str(), vl); 
            if(ret == -1)
            {
                std::cout << "vasprintf file!" << std::endl;
                return;
            }
            va_end(vl);
           serialize(LogLevel::value::WARN, file, line, tmp);
           free(tmp);
        }
        void error(const std::string& file, size_t line, const std::string& fmt, ...)
        {
            if(LogLevel::value::ERROR < _limit_level) return;
            va_list vl;
            va_start(vl, fmt);
            char* tmp; 
            int ret = vasprintf(&tmp, fmt.c_str(), vl); 
            if(ret == -1)
            {
                std::cout << "vasprintf file!" << std::endl;
                return;
            }
            va_end(vl);
           serialize(LogLevel::value::ERROR, file, line, tmp);
           free(tmp);
        }
        void fatal(const std::string& file, size_t line, const std::string& fmt, ...)
        {
            if(LogLevel::value::FATAL < _limit_level) return;
            va_list vl;
            va_start(vl, fmt);
            char* tmp; 
            int ret = vasprintf(&tmp, fmt.c_str(), vl); 
            if(ret == -1)
            {
                std::cout << "vasprintf file!" << std::endl;
                return;
            }
            va_end(vl);
           serialize(LogLevel::value::FATAL, file, line, tmp);
           free(tmp);
        }
    protected:
        void serialize(LogLevel::value level, const std::string& file, size_t line, char* str)
        {
            //3.构造LogMsg对象
            LogMsg msg(_logger_name, file, line, level, str);
            //4.通过格式化工具对LogMsg进行格式化，得到格式化后的日志字符串
            std::stringstream ss;
            _formatter->format(ss, msg);
            //5.进行日志落地
            log(ss.str().c_str(), ss.str().size());
        }
        virtual void log(const char* data, size_t len) = 0;
    protected:
        std::mutex _mutex;
        std::string _logger_name;
        std::atomic<LogLevel::value> _limit_level;
        Formatter::ptr _formatter;
        std::vector<LogOut::ptr> _outs; // 同一条日志信息可能会落地到多个方向
    };

    class SyncLogger : public Logger {
    public:
        using ptr = std::shared_ptr<SyncLogger>;
        SyncLogger(const std::string &name, 
            Formatter::ptr formatter,
            std::vector<LogOut::ptr> &outs, 
            LogLevel::value level = LogLevel::value::DEBUG)
        : Logger(name, formatter, outs, level)
        { 
            std::cout << LogLevel::tostring(level) << " 同步日志器: " << name << "创建成功...\n";
        }
    private:
         void log(const char* data, size_t len) override
         {
            std::unique_lock<std::mutex> lock(_mutex);
            if (_outs.empty()) return;
            for (auto& it : _outs) {
                it->log(data, len);
            }
        }
    };

    class AsyncLogger : public Logger
    {
    public:
        AsyncLogger(const std::string &name, 
            Formatter::ptr formatter,
            std::vector<LogOut::ptr> &outs, 
            LogLevel::value level = LogLevel::value::DEBUG,
            AsyncType worker_type = AsyncType::SAFE)
        : Logger(name, formatter, outs, level)
        , _worker(std::make_shared<AsynWorker>(std::bind(&AsyncLogger::realLogOut, this, std::placeholders::_1), worker_type))
        { 
            std::cout << LogLevel::tostring(level) << " 异步日志器: " << name << "创建成功...\n";
        }
        // 将数据写入缓冲区
        void log(const char* data, size_t len) override
        {
            _worker->push(data, len);
        }
        // 实际落地函数（将缓冲区的数据落地）
        void realLogOut(Buffer& buf) // 异步工作器那里的回调函数
        {
            if (_outs.empty()) return;
            for (auto& it : _outs) {
                it->log(buf.begin(), buf.readAbleSize());
            }
        }
    private:
        AsynWorker::ptr _worker;
    };

    enum class LoggerType
    {
        LOGGER_SYNC,
        LOGGER_ASYNC
    };
    /*使用建造者模式来建造日志器，而不要让用户直接去构造日志器，简化用户的使用复杂度*/
    //1.抽象一个日志器建造者类(完成日志器对象所需零部件的构建 & 日志器的构建)
        // 1.设置日志器类型
        // 2.将不同类型日志器的创建放到同一个日志器建造者类中完成
    //2.派生出具体的建造者类---局部日志器的建造者 & 全局的日志器建造者(后边添加了全局单例管理器之后，将日志器添加全局管理)
    class LoggerBuilder
    {
    public:
        LoggerBuilder()
        :_limit_level(LogLevel::value::DEBUG)
        ,_logger_type(LoggerType::LOGGER_SYNC) // 默认是同步方式
        ,_worker_type(AsyncType::SAFE)
        {}
        void buildLoggerType(LoggerType type){ _logger_type = type; }
        void buildLoggerName(const std::string& name){ _logger_name = name; }
        void buildLoggerLimitLevel(LogLevel::value level){ _limit_level = level; }
        void buildEnUnsafeAsync() { _worker_type = AsyncType::UNSAFE; }
        void buildFormatter(const std::string& pattern)
        {
            _formatter = std::make_shared<Formatter>(pattern);
        }
        template<class OutType, class ...Args>
        void buildOut(Args&& ...args)
        {
            LogOut::ptr pout = OutFactory::create<OutType>(std::forward<Args>(args)...);
            _outs.push_back(pout);
        }

        virtual Logger::ptr build() = 0;
    protected:
        AsyncType _worker_type;
        LoggerType _logger_type;
        std::string _logger_name;
        LogLevel::value _limit_level;
        Formatter::ptr _formatter;
        std::vector<LogOut::ptr> _outs;
    };
    
    // 2.派生出具体的建造者类---局部日志器的建造者 & 全局的日志器建造者(后边添加了全局单例管理器之后，将日志器添加全局管理)
    class LocalLoggerBuilder : public LoggerBuilder
    {
    public:
        Logger::ptr build()override
        {
            if(_logger_name.empty())  // 必须要有日志器名称
            {
                std::cout << "日志器名称不能为空！！";
                abort();
            }
            if(_formatter.get() == nullptr) // .get() 智能指针的用法，通常用于检查是否为空
            {
                std::cout << "当前日志器：" << _logger_name << " 未检测到日志格式，设为默认值" << std::endl;
                _formatter = std::make_shared<Formatter>();
            }
            if(_outs.empty()) {
                std::cout << "当前日志器：" << _logger_name << " 未检测到落地方向，默认设置为标准输出!" << std::endl;
                _outs.push_back(std::make_shared<StdoutOut>());
                // buildOut<StdoutOut>();
            }
            if(_logger_type == LoggerType::LOGGER_ASYNC)
            {
                return std::make_shared<AsyncLogger>(_logger_name, _formatter, _outs, _limit_level, _worker_type);
            }
            return std::make_shared<SyncLogger>(_logger_name, _formatter, _outs, _limit_level);
        }
    };

    class LoggerManager
    {
    public:
        static LoggerManager& getInstance()
        {
            static LoggerManager eton; 
            return eton;
        }
        // 判断日志器是否存在
        bool hasLogger(const std::string& name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _loggers.find(name);
            if(it == _loggers.end()) return false;
            return true;
        }
        // 添加日志器
        void addLogger(Logger::ptr& logger)
        {
            if(hasLogger(logger->loggerName())) return;
            std::unique_lock<std::mutex> lock(_mutex);
            _loggers.insert(std::make_pair(logger->loggerName(), logger));
        }
        // 拿到日志器
        Logger::ptr getLogger(const std::string& name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _loggers.find(name);
            if(it == _loggers.end()) return Logger::ptr();
            return it->second;
        }
        // 默认日志器
        Logger::ptr rootLogger()
        {
            return _root_logger;
        }
    private:
        // 构造私有化
        LoggerManager()
        {
            std::unique_ptr<LoggerBuilder> builder(new LocalLoggerBuilder());
            builder->buildLoggerName("root");
            _root_logger = builder->build(); //构建默认的日志器
            _loggers.insert(std::make_pair("root", _root_logger));
        }
    private:
        std::mutex _mutex;
        std::unordered_map<std::string, Logger::ptr> _loggers;
        Logger::ptr _root_logger; //默认日志器
    };

    // 全局日志器建造者-- 在局部的基础上增加了一个功能：将日志器添加到单例对象中
    class GlobalLoggerBuilder : public LoggerBuilder
    {
    public:
        Logger::ptr build()override
        {
            if(_logger_name.empty())  // 必须要有日志器名称
            {
                std::cout << "日志器名称不能为空！！";
                abort();
            }
            if(_formatter.get() == nullptr) // .get() 智能指针的用法，通常用于检查是否为空
            {
                std::cout << "当前日志器：" << _logger_name << " 未检测到日志格式，设为默认值" << std::endl;
                _formatter = std::make_shared<Formatter>();
            }
            if(_outs.empty()) {
                std::cout << "当前日志器：" << _logger_name << " 未检测到落地方向，默认设置为标准输出!" << std::endl;
                _outs.push_back(std::make_shared<StdoutOut>());
                // buildOut<StdoutOut>();
            }
            Logger::ptr logger;
            if(_logger_type == LoggerType::LOGGER_ASYNC)
                logger = std::make_shared<AsyncLogger>(_logger_name, _formatter, _outs, _limit_level, _worker_type);
            else
                logger = std::make_shared<SyncLogger>(_logger_name, _formatter, _outs, _limit_level);
            LoggerManager::getInstance().addLogger(logger);
            return logger;
        }
    };
}